contents
PostgreSQL에서 jsonb 는 JSON(JavaScript Object Notation) 데이터를 분해된 바이너리 형식으로 저장하는 데 사용되는 데이터 타입입니다. 이는 텍스트 기반의 형제인 json 타입보다 효율적인 쿼리 및 인덱싱에 고도로 최적화되어 있어, 대부분의 시나리오에서 JSON 데이터를 처리하기 위한 우선적인 선택지입니다.
핵심적인 차이: json vs. jsonb ⚖️
PostgreSQL은 JSON 데이터를 저장하기 위한 두 가지 데이터 타입을 제공하며, 그 차이를 이해하는 것이 매우 중요합니다.
1. json (텍스트 기반)
- 저장 방식:
json타입은 입력된 텍스트의 있는 그대로의 정확한 복사본을 저장합니다. 모든 공백, 객체 내 키의 원래 순서, 그리고 중복된 키까지 보존합니다. - 성능:
- 데이터베이스가 아무런 처리도 하지 않고 텍스트를 그대로 저장하므로 입력 속도는 매우 빠릅니다.
- 쿼리를 실행할 때마다 전체 JSON 텍스트를 다시 파싱해야 하므로 쿼리 속도는 매우 느립니다.
- 비유: 책의 각 페이지를 사진으로 찍어 저장하는 것과 같습니다. 사진을 저장하는 것은 빠르지만, 특정 단어를 찾으려면 매번 페이지 전체를 처음부터 끝까지 읽어야 합니다.
2. jsonb (바이너리 기반)
- 저장 방식:
jsonb타입은 입력된 텍스트를 분해된 바이너리 형식으로 변환하여 저장합니다. 이는 공백, 키의 순서, 중복된 키를 보존하지 않습니다(중복된 키의 경우 마지막 값만 유지합니다). - 성능:
- 입력된 텍스트를 파싱하고 변환해야 하므로 입력 속도는
json타입보다 약간 느립니다. - 데이터가 이미 파싱되어 효율적으로 접근하고 인덱싱할 수 있는 형식이므로 쿼리 속도는 극적으로 빠릅니다.
- 입력된 텍스트를 파싱하고 변환해야 하므로 입력 속도는
- 비유: 책의 내용을 검색 가능한 컴퓨터 문서로 타이핑하여 저장하는 것과 같습니다. 초기 타이핑에는 시간이 걸리지만, 일단 완료되면 어떤 단어든 거의 즉시 검색할 수 있습니다.
| 특징 | json |
jsonb |
|---|---|---|
| 저장 방식 | 일반 텍스트 | 분해된 바이너리 형식 |
| 입력 속도 | 더 빠름 (처리 없음) | 더 느림 (파싱 필요) |
| 쿼리 속도 | 느림 (매 쿼리마다 재파싱) | 훨씬 빠름 (이미 파싱 및 인덱싱됨) |
| 포맷 보존 | 공백, 키 순서, 중복 키 보존 | 공백, 키 순서, 중복 키 보존 안 함 |
| 권장 사항 | 유효성 검사나 레거시 시스템에만 사용 | 거의 모든 새로운 애플리케이션에 사용 |
jsonb 사용법: 쿼리 및 연산자 🚀
products 테이블에 jsonb 컬럼으로 제품 메타데이터를 저장한다고 가정해 봅시다.
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT NOT NULL,
data JSONB
);
INSERT INTO products (name, data) VALUES
('노트북', '{ "price": 1200, "brand": "TechCorp", "specs": { "ram": 16, "storage": 512 }, "tags": ["electronics", "computer"] }'),
('마우스', '{ "price": 25, "brand": "TechCorp", "specs": { "wireless": true }, "tags": ["electronics", "accessory"] }');
주요 연산자
->: 키로 필드 가져오기. 값을jsonb타입으로 반환합니다.
-- 노트북의 'specs' 객체 가져오기
SELECT data -> 'specs' FROM products WHERE name = '노트북';
-- 결과: { "ram": 16, "storage": 512 } (jsonb 타입)
->>: 키로 필드를text타입으로 가져오기.WHERE절에서 사용하기 위해 매우 중요합니다.
-- 'TechCorp' 브랜드의 제품 찾기
SELECT name FROM products WHERE data ->> 'brand' = 'TechCorp';
-- 결과: 노트북, 마우스
#>: 특정 경로의 값 가져오기.
-- 노트북의 RAM 크기 가져오기
SELECT data #> '{specs,ram}' FROM products WHERE name = '노트북';
-- 결과: 16 (jsonb 타입)
#>>: 특정 경로의 값을text타입으로 가져오기.
-- RAM이 16GB인 제품 찾기
SELECT name FROM products WHERE data #>> '{specs,ram}' = '16';
-- 결과: 노트북
@>: 포함 연산자 (Contains). 가장 강력한 연산자 중 하나입니다. 왼쪽jsonb값이 오른쪽jsonb값을 포함하는지 확인합니다.
-- 'TechCorp'에서 만든 모든 제품 찾기
SELECT name FROM products WHERE data @> '{"brand": "TechCorp"}';
-- 무선 기능이 있는 모든 제품 찾기
SELECT name FROM products WHERE data @> '{"specs": {"wireless": true}}';
jsonb 컬럼 인덱싱 (성능의 핵심)
jsonb 컬럼에 대한 쿼리를 빠르게 만들려면 반드시 인덱스를 생성해야 합니다. 인덱스가 없으면 PostgreSQL은 전체 테이블을 스캔하여 모든 행을 읽고 그 안의 JSONB 데이터를 파싱해야 합니다.
GIN (Generalized Inverted Index)
GIN 인덱스는 jsonb 데이터에 가장 일반적이고 강력한 인덱스 유형입니다. 각 JSONB 도큐먼트 내의 키와 값에 대한 인덱스를 생성합니다.
GIN 인덱스 생성:
CREATE INDEX idx_products_data ON products USING GIN (data);
이 단일 인덱스는 다음과 같은 키 존재 여부 및 포함 연산자를 사용하는 쿼리의 속도를 높여줍니다.
@>(포함)?(키 존재)?|(배열의 키 중 하나라도 존재)?&(배열의 모든 키가 존재)
예를 들어, GIN 인덱스를 생성한 후 다음 쿼리는 매우 빨라집니다.
-- 이 쿼리는 이제 전체 테이블 스캔 대신 GIN 인덱스를 사용합니다.
SELECT name FROM products WHERE data @> '{"brand": "TechCorp"}';
결론적으로, jsonb는 PostgreSQL에 상당한 이점을 제공하는 강력한 기능으로, NoSQL 문서 저장소의 유연성과 선도적인 관계형 데이터베이스의 트랜잭션 무결성 및 성숙한 생태계를 효과적으로 결합합니다.
references